package com.gcloud.mesh.dcs.service;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.jeecg.common.exception.ParamException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import com.gcloud.framework.db.PageResult;
import com.gcloud.mesh.asset.service.IAssetService;
import com.gcloud.mesh.dcs.dao.DataEnergyDao;
import com.gcloud.mesh.dcs.entity.DataEnergyEntity;
import com.gcloud.mesh.header.enums.CalendarMonth;
import com.gcloud.mesh.header.exception.BaseException;
import com.gcloud.mesh.header.exception.DatacenterEnergyErrorCode;
import com.gcloud.mesh.header.msg.asset.ListDatacenterMsg;
import com.gcloud.mesh.header.msg.dcs.ReportByMonthMsg;
import com.gcloud.mesh.header.msg.dcs.ReportByQuarterMsg;
import com.gcloud.mesh.header.msg.dcs.ReportByYearMsg;
import com.gcloud.mesh.header.msg.dcs.StatisticByMonthMsg;
import com.gcloud.mesh.header.msg.dcs.StatisticByQuarterMsg;
import com.gcloud.mesh.header.msg.dcs.StatisticByYearMsg;
import com.gcloud.mesh.header.vo.asset.DatacenterItemVo;
import com.gcloud.mesh.header.vo.dcs.DataEnergyVo;
import com.gcloud.mesh.header.vo.dcs.DatacenterEnergyVo;
import com.gcloud.mesh.header.vo.dcs.DatacenterReportVo;
import com.gcloud.mesh.header.vo.dcs.StatisticsVo;
import com.gcloud.mesh.utils.MeshMathUtil;
import com.gcloud.mesh.utils.PageUtil;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class DatacenterEnergyService {
	
	
	@Autowired
	private DataEnergyDao dataEnergyDao;
	
	@Autowired
	private IAssetService assetService;

	

	public DatacenterEnergyVo statisticsByYear(StatisticByYearMsg msg) throws Exception {
		DatacenterEnergyVo res = new DatacenterEnergyVo();
		
		//日期初始化
		List<DataEnergyVo> finalData = listMonthByYear(Integer.parseInt(msg.getYear())).stream()
										.map( s -> new DataEnergyVo(s, 0d, 0L, msg.getDatacenterId()))
										.collect(Collectors.toList());
		//实际得到每月的值
		List<DataEnergyVo> data = dataEnergyDao.listByMonth(msg.getDatacenterId(), msg.getYear(), DataEnergyVo.class);
		
    	data.stream().forEach( s -> {
    		String[] tmp = s.getLabel().split("-");
    		int year = Integer.valueOf(tmp[0]);
    		int month = Integer.valueOf(tmp[1]);
    		
    		List<String> days = listNowDaysByMonth(year, month);
    		s.setValue(MeshMathUtil.setScale(2, s.getValue()/s.getCount()*days.size()));
//    		s.setCount((long)s.getCount()/data.size());
    	});

		double avg = data.stream().mapToDouble( s -> s.getValue()).average().orElse(0d);
		double max = data.stream().mapToDouble( s -> s.getValue()).max().orElse(0d);
		double min = data.stream().mapToDouble( s -> s.getValue()).min().orElse(0d);
		StatisticsVo statistics = new StatisticsVo();
		statistics.setAvg(MeshMathUtil.setScale(2, avg));
		statistics.setMax(MeshMathUtil.setScale(2, max));
		statistics.setMin(MeshMathUtil.setScale(2, min));
		
		filledData(data, finalData);
		res.setStatistics(statistics);
		res.setList(finalData);

		return res;
    }
	
	public DatacenterEnergyVo statisticsByQuarter(StatisticByQuarterMsg msg) throws Exception {
		DatacenterEnergyVo res = new DatacenterEnergyVo();
		
		List<CalendarMonth> cals = CalendarMonth.listByQuarter(msg.getQuarter());
		if(cals == null || cals.size() == 0) {
			throw new ParamException(DatacenterEnergyErrorCode.QUARTER_FORMAT_ERROR);
		}
		
		//日期初始化
		List<DataEnergyVo> finalData = listMonthByQuarter(Integer.parseInt(msg.getYear()), msg.getQuarter()).stream()
										.map( s -> new DataEnergyVo(s, 0d, 0L, msg.getDatacenterId()))
										.collect(Collectors.toList());
		//实际得到每月的值
		List<DataEnergyVo> data = dataEnergyDao.listByMonth(msg.getDatacenterId(), msg.getYear(), DataEnergyVo.class).stream()
									.filter(s -> filterByQuarter(s.getLabel(), msg.getQuarter())).collect(Collectors.toList());
    	data.stream().forEach( s -> {
    		String[] tmp = s.getLabel().split("-");
    		int year = Integer.valueOf(tmp[0]);
    		int month = Integer.valueOf(tmp[1]);
    		
    		List<String> days = listNowDaysByMonth(year, month);
    		s.setValue(MeshMathUtil.setScale(2, s.getValue()/s.getCount()*days.size()));
    	});
    	
		double avg = data.stream().mapToDouble( s -> s.getValue()).average().orElse(0d);
		double max = data.stream().mapToDouble( s -> s.getValue()).max().orElse(0d);
		double min = data.stream().mapToDouble( s -> s.getValue()).min().orElse(0d);
		StatisticsVo statistics = new StatisticsVo();
		statistics.setAvg(MeshMathUtil.setScale(2, avg));
		statistics.setMax(MeshMathUtil.setScale(2, max));
		statistics.setMin(MeshMathUtil.setScale(2, min));
		
		filledData(data, finalData);
		res.setStatistics(statistics);
		res.setList(finalData);
		
		return res;
    }
	
	public DatacenterEnergyVo statisticsByMonth(StatisticByMonthMsg msg) throws Exception {
		DatacenterEnergyVo res = new DatacenterEnergyVo();
		
		if(CalendarMonth.getByNo(msg.getMonth()) == null) {
			throw new ParamException(DatacenterEnergyErrorCode.MONTH_FORMAT_ERROR);
		}
		
		//日期初始化
		List<DataEnergyVo> finalData = listDaysByMonth(Integer.parseInt(msg.getYear()), msg.getMonth()).stream()
										.map( s -> new DataEnergyVo(s, 0d, 0L, msg.getDatacenterId()))
										.collect(Collectors.toList());
		//实际得到每天的值
		String month = CalendarMonth.getByNo(msg.getMonth()).getNum();
		List<DataEnergyVo> data = dataEnergyDao.listByDay(msg.getDatacenterId(), msg.getYear(), month, DataEnergyVo.class);
		valueToAvg(data);
		double avg = data.stream().mapToDouble( s -> s.getValue()).average().orElse(0d);
		double max = data.stream().mapToDouble( s -> s.getValue()).max().orElse(0d);
		double min = data.stream().mapToDouble( s -> s.getValue()).min().orElse(0d);
		StatisticsVo statistics = new StatisticsVo();
		statistics.setAvg(MeshMathUtil.setScale(2, avg));
		statistics.setMax(MeshMathUtil.setScale(2, max));
		statistics.setMin(MeshMathUtil.setScale(2, min));		
		
		filledData(data, finalData);
		res.setStatistics(statistics);
		res.setList(finalData);

		return res;
    }
	
	public PageResult<DatacenterReportVo> reportByYear(ReportByYearMsg msg) throws Exception {

		List<DatacenterReportVo> res = new ArrayList<DatacenterReportVo>();
//		List<String> years = Arrays.asList("2010", "2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019", "2020");
		
		ListDatacenterMsg listDatacenterMsg = new ListDatacenterMsg();
		Map<String, String> dcMap = assetService.listDatacenter(listDatacenterMsg).stream().collect(Collectors.toMap(DatacenterItemVo::getId, s -> s.getName()));
		
		List<String> years = null;
		if(msg.getYear() != null) {
			years = Arrays.asList(msg.getYear());
		}else {
			years = Arrays.asList("2018", "2019", "2020", "2021");
		}
		
		List<String> datacenters = null;
		if(msg.getDatacenterId() != null) {
			datacenters = Arrays.asList(msg.getDatacenterId());
		}else {
			datacenters = dcMap.entrySet().stream().map( s -> s.getKey().toString()).collect(Collectors.toList());
		}
		
		for(String datacenterId: datacenters) {
			List<DataEnergyVo> alls = dataEnergyDao.listByMonth(datacenterId, null, DataEnergyVo.class);
			for(String year: years) {
				//日期初始化
//				List<DataEnergyVo> finalData = listMonthByYear(Integer.parseInt(msg.getYear())).stream()
//												.map( s -> new DataEnergyVo(s, 0d, 0L, msg.getDatacenterId()))
//												.collect(Collectors.toList());
				//实际得到每月的值
//				List<DataEnergyVo> data = dataEnergyDao.listByMonth(datacenterId, year, DataEnergyVo.class);
				String filter = year;
				List<DataEnergyVo> data = alls.stream().filter( s -> s.getLabel().indexOf(filter) == 0).collect(Collectors.toList());
		    	data.stream().forEach( s -> {
		    		String[] tmp = s.getLabel().split("-");
		    		int yearYear = Integer.valueOf(tmp[0]);
		    		int month = Integer.valueOf(tmp[1]);
		    		
		    		List<String> days = listNowDaysByMonth(yearYear, month);
		    		s.setValue(MeshMathUtil.setScale(2, s.getValue()/s.getCount()*days.size()));
		    	});

				double avg = data.stream().mapToDouble( s -> s.getValue()).average().orElse(0d);
				double max = data.stream().mapToDouble( s -> s.getValue()).max().orElse(0d);
				double min = data.stream().mapToDouble( s -> s.getValue()).min().orElse(0d);
				
				if(avg == 0 && max == 0 && min == 0 ) {
					continue;
				}
				
				DatacenterReportVo report = new DatacenterReportVo();
				report.setAvg(MeshMathUtil.setScale(2, avg));
				report.setMax(MeshMathUtil.setScale(2, max));
				report.setMin(MeshMathUtil.setScale(2, min));
				report.setLabel(String.format("%s年", year));
				report.setDatacenterName(dcMap.get(datacenterId));
				res.add(report);
			}
		}

		// 排序
		sort(res);
		
		return PageUtil.page(msg.getPageNo(), msg.getPageSize(), res);
    }
	
	public PageResult<DatacenterReportVo> reportByQuarter(ReportByQuarterMsg msg) throws Exception {
		
		List<DatacenterReportVo> res = new ArrayList<DatacenterReportVo>();
		
//		List<String> years = Arrays.asList("2010", "2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019", "2020");
		
		ListDatacenterMsg listDatacenterMsg = new ListDatacenterMsg();
		Map<String, String> dcMap = assetService.listDatacenter(listDatacenterMsg).stream().collect(Collectors.toMap(DatacenterItemVo::getId, s -> s.getName()));
		
		Map<Integer, String> quarterMap = new HashMap<Integer, String>();
		quarterMap.put(1, "一");
		quarterMap.put(2, "二");
		quarterMap.put(3, "三");
		quarterMap.put(4, "四");
		
		List<String> years = null;
		if(msg.getYear() != null) {
			years = Arrays.asList(msg.getYear());
		}else {
			years = Arrays.asList("2018", "2019", "2020", "2021");
		}
		
		List<Integer> quarters = null;
		if(msg.getQuarter() != 0) {
			List<CalendarMonth> cals = CalendarMonth.listByQuarter(msg.getQuarter());
			if(cals == null || cals.size() == 0) {
				throw new ParamException(DatacenterEnergyErrorCode.QUARTER_FORMAT_ERROR);
			}
			quarters = Arrays.asList(msg.getQuarter());
		}else {
			quarters = Arrays.asList(1, 2, 3, 4);
		}
		
		List<String> datacenters = null;
		if(msg.getDatacenterId() != null) {
			datacenters = Arrays.asList(msg.getDatacenterId());
		}else {
			datacenters = dcMap.entrySet().stream().map( s -> s.getKey().toString()).collect(Collectors.toList());
		}
		
		for(String datacenterId: datacenters) {
			List<DataEnergyVo> alls = dataEnergyDao.listByMonth(datacenterId, null, DataEnergyVo.class);
			for(String year: years) {
				for(Integer quarter: quarters) {
					List<CalendarMonth> cals = CalendarMonth.listByQuarter(quarter);
					if(cals == null || cals.size() == 0) {
						throw new ParamException(DatacenterEnergyErrorCode.QUARTER_FORMAT_ERROR);
					}
					
					//日期初始化
//					List<DataEnergyVo> finalData = listMonthByQuarter(Integer.parseInt(msg.getYear()), msg.getQuarter()).stream()
//													.map( s -> new DataEnergyVo(s, 0d, 0L, msg.getDatacenterId()))
//													.collect(Collectors.toList());
					//实际得到每月的值
//					List<DataEnergyVo> data = dataEnergyDao.listByMonth(datacenterId, year, DataEnergyVo.class).stream()
//												.filter(s -> filterByQuarter(s.getLabel(), quarter)).collect(Collectors.toList());
					String filter = year;
					List<DataEnergyVo> dataTemp = alls.stream().filter( s -> s.getLabel().indexOf(filter) == 0).collect(Collectors.toList());
					List<DataEnergyVo> data = dataTemp.stream()
							.filter(s -> filterByQuarter(s.getLabel(), quarter)).collect(Collectors.toList());
			    	data.stream().forEach( s -> {
			    		String[] tmp = s.getLabel().split("-");
			    		int yearTemp = Integer.valueOf(tmp[0]);
			    		int month = Integer.valueOf(tmp[1]);
			    		
			    		List<String> days = listNowDaysByMonth(yearTemp, month);
			    		s.setValue(MeshMathUtil.setScale(2, s.getValue()/s.getCount()*days.size()));
			    	});
					double avg = data.stream().mapToDouble( s -> s.getValue()).average().orElse(0d);
					double max = data.stream().mapToDouble( s -> s.getValue()).max().orElse(0d);
					double min = data.stream().mapToDouble( s -> s.getValue()).min().orElse(0d);
					
					if(avg == 0 && max == 0 && min == 0 ) {
						continue;
					}
					
					DatacenterReportVo report = new DatacenterReportVo();
					report.setAvg(MeshMathUtil.setScale(2, avg));
					report.setMax(MeshMathUtil.setScale(2, max));
					report.setMin(MeshMathUtil.setScale(2, min));		
					report.setLabel(String.format("%s年第%s季度", year, quarterMap.get(quarter)));
					report.setDatacenterName(dcMap.get(datacenterId));
					res.add(report);
				}
			}
		}

		
		// 排序
		sort(res);
		
		return PageUtil.page(msg.getPageNo(), msg.getPageSize(), res);
    }
	
	public PageResult<DatacenterReportVo> reportByMonth(ReportByMonthMsg msg) throws Exception {
		
		List<DatacenterReportVo> res = new ArrayList<DatacenterReportVo>();
//		List<String> years = Arrays.asList("2010", "2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019", "2020");
//		List<String> years = Arrays.asList("2018", "2019", "2020");
//		List<String> months = Arrays.asList("01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12");		

		ListDatacenterMsg listDatacenterMsg = new ListDatacenterMsg();
		Map<String, String> dcMap = assetService.listDatacenter(listDatacenterMsg).stream().collect(Collectors.toMap(DatacenterItemVo::getId, s -> s.getName()));
		
		List<String> years = null;
		if(msg.getYear() != null) {
			years = Arrays.asList(msg.getYear());
		}else {
			years = Arrays.asList("2018", "2019", "2020", "2021");
		}
		
		List<String> months = null;
		if(msg.getMonth() != 0) {
			if(CalendarMonth.getByNo(msg.getMonth()) == null) {
				throw new ParamException(DatacenterEnergyErrorCode.MONTH_FORMAT_ERROR);
			}
			months = Arrays.asList(CalendarMonth.getByNo(msg.getMonth()).getNum());
		}else {
			months = Arrays.asList("01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12");
		}
		
		List<String> datacenters = null;
		if(msg.getDatacenterId() != null) {
			datacenters = Arrays.asList(msg.getDatacenterId());
		}else {
			datacenters = dcMap.entrySet().stream().map( s -> s.getKey().toString()).collect(Collectors.toList());
		}
		
		for(String datacenterId: datacenters) {
			List<DataEnergyVo> alls = dataEnergyDao.listByDay(datacenterId, null, null, DataEnergyVo.class);
			for(String year: years) {
				for(String month: months) {
					
					//实际得到每天的值
//					List<DataEnergyVo> data = dataEnergyDao.listByDay(datacenterId, year, month, DataEnergyVo.class);
					String filter = year + "-" + month;
					List<DataEnergyVo> data = alls.stream().filter( s -> s.getLabel().indexOf(filter) == 0).collect(Collectors.toList());
					valueToAvg(data);
					double avg = data.stream().mapToDouble( s -> s.getValue()).average().orElse(0d);
					double max = data.stream().mapToDouble( s -> s.getValue()).max().orElse(0d);
					double min = data.stream().mapToDouble( s -> s.getValue()).min().orElse(0d);
					if(avg == 0 && max == 0 && min == 0 ) {
						continue;
					}
					
					DatacenterReportVo report = new DatacenterReportVo();
					report.setAvg(MeshMathUtil.setScale(2, avg));
					report.setMax(MeshMathUtil.setScale(2, max));
					report.setMin(MeshMathUtil.setScale(2, min));		
					report.setLabel(String.format("%s年%s月", year, CalendarMonth.getByNum(month).getNo()));
					report.setDatacenterName(dcMap.get(datacenterId));
					res.add(report);
				}
			}
		}

		// 排序
		sort(res);
		
		return PageUtil.page(msg.getPageNo(), msg.getPageSize(), res);
    }

    
    //每分钟采集一次数据，并定时更新数据库
	@Scheduled(initialDelay = 60, fixedDelay = 60 * 1000)
    private void updateStatistics() {
    	
    	ListDatacenterMsg msg = new ListDatacenterMsg();
    	List<DatacenterItemVo> datacenters = assetService.listDatacenter(msg);
    	for(DatacenterItemVo vo: datacenters) {
    		double value = vo.getPower();
    		long count = 1;
        	savePoint(value, count, vo.getId(), new Date());
    	}
    }
    
    private void savePoint(Double value, Long count, String datacenterId, Date createTime) {
    	DataEnergyEntity point = new DataEnergyEntity();
    	point.setId(UUID.randomUUID().toString());
    	point.setTimestamp(createTime);
    	point.setValue(value);
    	point.setCount(count);
    	point.setDatacenterId(datacenterId);
    	try {
    		dataEnergyDao.save(point);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			log.error("[DatacenterEnergyService][savePoint] 数据保存失败：{}", e.getMessage());
		}
    }
    
    private boolean filterByQuarter(String monthStr, int quarter) {
    	int month = Integer.parseInt(monthStr.substring(5));
    	List<Integer> months = CalendarMonth.listByQuarter(quarter).stream().map( s -> s.getNo()).collect(Collectors.toList());
    	return months.contains(month);
    }
    
    private void sort(List<DatacenterReportVo> data) {
    	
    	Collections.sort(data, new Comparator<DatacenterReportVo>() {

			@Override
			public int compare(DatacenterReportVo o1, DatacenterReportVo o2) {
				// TODO Auto-generated method stub
				return -o1.getLabel().compareTo(o2.getLabel());
			}
			
		});
    }
    
    private void valueToAvg(List<DataEnergyVo> data) {
    	
    	data.stream().forEach( s -> {
    		s.setValue(MeshMathUtil.setScale(2, s.getValue()/s.getCount()));
//    		s.setCount((long)s.getCount()/data.size());
    	});
    }
    
    private List<DataEnergyVo> listAvgs(List<DataEnergyVo> data) {
    	return null;
    }
    
    private List<String> listDaysByMonth(int year, int month) {
    	List<String> days = new ArrayList<String>();
    	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    	Calendar cal = Calendar.getInstance();
    	cal.set(Calendar.YEAR, year);
    	cal.set(Calendar.MONTH, month - 1);
    	cal.set(Calendar.DATE, 1);
    	cal.roll(Calendar.DATE, -1);
    	int max = cal.get(Calendar.DATE);
    	cal.setTime(new Date());
    	cal.set(Calendar.YEAR, year);
    	cal.set(Calendar.MONTH, month - 1);
    	cal.set(Calendar.DAY_OF_MONTH, 1);
    	while(max != 0) {
    		days.add(sdf.format(cal.getTime()));
    		max = max - 1;
    		cal.add(Calendar.DAY_OF_MONTH, 1);
    	}
    	return days;
    	
    }
    
    private List<String> listNowDaysByMonth(int year, int month) {
    	
    	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    	Calendar cal = Calendar.getInstance();
    	Date now = new Date();
    	cal.setTime(now);
    	int nowMonth = cal.get(Calendar.MONTH) + 1;
    	if(nowMonth == month) {
    		List<String> days = new ArrayList<String>();
    		int count = cal.get(Calendar.DAY_OF_MONTH);
    		while(count != 0) {
    			days.add(sdf.format(cal.getTime()));
    			count = count - 1;
    			cal.add(Calendar.DAY_OF_MONTH, -1);
    		}
    		return days;
    	}
    	
    	return listDaysByMonth(year, month);
   	
    }
    
    private List<String> listMonthByQuarter(int year, int quarter) {
    	List<String> months = CalendarMonth.listByQuarter(quarter).stream()
    							.map( s -> {
    								return year + "-" + s.getNum();
    							})
    							.collect(Collectors.toList());
    	return months;
    }
    
    private List<String> listMonthByYear(int year) {
    	List<String> months = Stream.of(CalendarMonth.values())
    							.map( s -> {
    								return year + "-" + s.getNum();
    							})
    							.collect(Collectors.toList());
    	return months;
    }
    
    private void filledData(List<DataEnergyVo> data, List<DataEnergyVo> finalData) {
    	
    	Map<String, DataEnergyVo> map = data.stream().collect(Collectors.toMap(DataEnergyVo::getLabel, s -> s));
    	finalData.stream()
    	    .forEach( s -> {
    	    	if(map.get(s.getLabel()) != null) {
    	    		s.setValue(map.get(s.getLabel()).getValue());
    	    		s.setCount(map.get(s.getLabel()).getCount());
    	    	}
    	    });
    }
    
}
